// Extend.js (http://code.google.com/p/extendjs/)
// Copyright 2010 Shumpei Shiraishi All rights reserved.
// Licensed under the MIT license.

(function(_global) {
    var extendedClasses = {
    };
    var X = _global.X = function(value) {
	// already extended
	if (value && typeof value.isExtended === "function" && value.isExtended()) {
	    return value;
	}
	return extendedObject(value);
    };
    function extendedObject(value) {
	// user extended classes
	for (var className in extendedClasses) {
	    var type = extendedClasses[className];
	    if (type.isType(value)) {
		return new type.clazz(value);
	    }
	}
	return new X.Object(value);
    }
    // register type
    X.extend = function(className, isType) {
	var newClass = X[className] = XClass(className);
	extendedClasses[className] = {
	    isType: isType,
	    clazz: newClass
	};
	return newClass;
    };
    // class
    function XClass(className) {
	var clazz = function(value) {
	    this.__val = value;
	};
	clazz.getName = function() {
	    return className;
	};
	clazz.extend = function() {
	    extendClass.apply(clazz, X.util.toArray(arguments));
	};
	clazz.mixin = function() {
	    mixinClass.apply(clazz, X.util.toArray(arguments));
	};
	// inheritance from X.Object (if class is not X.Object itself)
	if (X.Object) {
	    clazz.extend(X.Object.prototype);
	}
	clazz.prototype.getClass = function() {
	    return clazz;
	};
	return clazz;
    }
    // X.Object.extend(name, method, generateX, override)
    // X.Object.extend(proto, generateX, override)
    function extendClass() {
	var name, method, proto, generateX, override;
	var toArray = X.util.toArray;
	if (typeof arguments[0] === "string") {
	    name = arguments[0];
	    method = arguments[1];
	    generateX = arguments[2];
	    override = arguments[3];
	} else {
	    proto = arguments[0];
	    generateX = arguments[1];
	    override = arguments[2];
	}
	if (proto) {
	    for (var p in proto) {
		extendClass.apply(this, [p, proto[p], generateX, override]);
	    }
	} else {
	    var original = this.prototype[name];
	    if (original && override === false) {
		throw new Error("cannot override this method:[" + name + "]");
	    }
	    this.prototype[name] = method;
	    if (generateX !== false) {
		this.prototype[name + "X"] = function() {
		    return X(method.apply(this, toArray(arguments)));
		};
	    }
	}
    }
    function mixinClass(proto, methodNames, generateX, override) {
	var each, clazz = this;
	if (methodNames instanceof Array) {
	    each = function(callback) {
		for (var i = 0, len = methodNames.length; i < len; i++) {
		    callback(methodNames[i]);
		}
	    };
	} else {
	    each = function(callback) {
		for (var methodName in type.prototype) {
		    callback(methodName);
		}
	    };
	}
	each(function(methodName) {
	    var original = clazz.prototype[methodName];
	    if (original && override === false) {
		throw new Error("cannot override this method:[" + methodName + "]");
	    }
	    var method = proto[methodName];
	    if (!method) {
		return;
	    }
	    clazz.prototype[methodName] = (function(_method) {
		return function() {
		    return _method.apply(this.__val, X.util.toArray(arguments));
		};
	    })(method);
	    if (generateX !== false) {
		clazz.prototype[methodName + "X"] = (function(_method) {
		    return function() {
			return X(_method.apply(this.__val, X.util.toArray(arguments)));
		    };
		})(method);
	    }
	});
    }
})(this);
